# -*- coding: utf-8 -*-
from __future__ import absolute_import
import os
import sys
import signal

# add up one level dir into sys path
sys.path.append(os.path.abspath(os.path.dirname(
    os.path.dirname(os.path.dirname(__file__)))))
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "base.settings")

import time
import json
import logging
from multiprocessing import Process, Manager

from common.cache import redis_cache
from common import orm
from common.timer import TIMER_EVENT_TYPE, cyclical_handler
from common.timer.handler import CouponExpiredHandler
from common.utils.tz import now_ts
from common.utils.pid_file import PIDFile
from common.utils import EnhencedEncoder
from common.lottery.cyclical import CYCLICAL_TYPE

from django.conf import settings
import logging.config

logging.config.dictConfig(settings.LOGGING)

_LOGGER = logging.getLogger('worker')

DEFAULT_EVENT_HANDLERS = {
    TIMER_EVENT_TYPE.ACTIVITY_STOP: cyclical_handler.ActivityStopHandler(),
    TIMER_EVENT_TYPE.CALC_STATS: cyclical_handler.ActivityStatsHandler(),
    TIMER_EVENT_TYPE.ACTIVITY_ANNOUNCE: cyclical_handler.ActivityAnnounceHandler(),
    TIMER_EVENT_TYPE.COUPON_EXPIRED: CouponExpiredHandler()
}

try:
    for t in CYCLICAL_TYPE:
        if t in cyclical_handler.NEXT_TERM:
            DEFAULT_EVENT_HANDLERS[cyclical_handler.NEXT_TERM[t]] = cyclical_handler.ActivityNextHandler(t)
        else:
            DEFAULT_EVENT_HANDLERS[cyclical_handler.SEQ_NEXT_TERM[t]] = cyclical_handler.SeqActivityNextHandler(t)
except Exception as e:
    _LOGGER.exception(e)
    raise e

_PROCESS_TIMEOUT = 30


class EventProcessor(object):
    """
    timer event processor
    """

    def __init__(self, event_type_list):
        self.event_type_list = event_type_list
        self.event_handlers = DEFAULT_EVENT_HANDLERS
        self.sleep_second = 0.5

    def get_expired_events(self, event_type):
        event_list = []
        try:
            max_time = now_ts()
            value_list = redis_cache.range_expired_events(event_type, max_time)
            for event_value in value_list:
                event_list.append(event_value)
        except Exception as e:
            _LOGGER.exception('get_expired_events failed.(%s)' % e)

        return event_list

    def process(self, event_type):
        _LOGGER.info("begin processing timer event type: %s",
                     TIMER_EVENT_TYPE.get_label(event_type))
        event_handler = self.event_handlers.get(event_type)
        if not event_handler:
            _LOGGER.error('event handler not found for %s', TIMER_EVENT_TYPE.get_label(event_type))
            return

        while infinite_process.value:
            events = self.get_expired_events(event_type)
            if not events:
                time.sleep(self.sleep_second)

            for event_value in events:
                try:
                    self._process(event_handler, event_type, event_value)
                except Exception as e:
                    _LOGGER.error('event value invalid.(%s:%s) %s' % (event_type, event_value, e))

        print u'quit processing: %s' % TIMER_EVENT_TYPE.get_label(event_type)

    @staticmethod
    def _process(event_handler, event_type, event_value):
        _LOGGER.info('event _process.(%s:%s)' % (event_type, event_value))
        event_msg = json.loads(event_value)

        def handler_process(event_id):
            success = event_handler.process(event_msg['msg'])
            # remove timer key
            redis_cache.remove_expired_event(event_type, event_value)
            if not success:
                try_count = event_msg.get('try_count', 0)
                max_try = event_handler.MAX_TRY
                if max_try == 0 or (  # 无限制
                        max_try and try_count < max_try):
                    try_count += 1
                    # 以2的指数次方退避，最多为1分钟
                    interval = min(2 ** try_count, 60)
                    next_exec_time = now_ts() + interval
                    event_msg['try_count'] = try_count
                    msg = json.dumps(event_msg, ensure_ascii=False,
                                     cls=EnhencedEncoder)
                    redis_cache.submit_timer_event(event_type, msg, next_exec_time)
                else:
                    event_handler.max_try_callback(event_msg)

        handler_process(event_msg['id'])

    def run(self):
        _LOGGER.info("timer processor run...")
        p_list = []
        for event_type in self.event_type_list:
            p = Process(target=self.process, args=(event_type,))
            p_list.append(p)
            p.start()

        for p in p_list:
            p.join()


if __name__ == "__main__":
    PIDFile('/tmp/bigbang.pid')
    manager = Manager()
    infinite_process = manager.Value('b', True)

    event_type_list = [k for k in TIMER_EVENT_TYPE.to_dict()]
    event_processor = EventProcessor(event_type_list)


    def handle_terminate(sig, frame):
        print 'wait to quit main process...'
        infinite_process.value = False
        orm.session.close()


    signal.signal(signal.SIGTERM, handle_terminate)
    event_processor.run()
